﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading;
using System.Windows.Forms;

namespace BookDownload
{
    /// <summary>
    /// 章节列表
    /// </summary>
    class ChapterList
    {
        //地址列表
        public ArrayList URL;
        //标题列表
        public ArrayList Title;

        public ChapterList()
        {
            URL = new ArrayList();
            Title = new ArrayList();
        }

    }
    /// <summary>
    /// 书籍的正则表达式
    /// </summary>
    class BookReg
    {
        public string charSet;
        //书籍名称正则表达式
        public string BookName;
        //获取章节列表正则表达式
        public string List;
        //获取章节内容正则表达式
        public string Content;
        //获取额外排除文本，以|间隔
        public string ExceptString;
        public BookReg()
        {
            charSet = "utf-8";
        }
    }
    /// <summary>
    /// 书籍下载类
    /// </summary>
    class BookLoad
    {
        mainForm myForm;
        string url;
        int startChapter;
        string charSet;
        delegate void SetTextCallback(string status, int amount, string message);//设置委托
        public BookLoad(mainForm _myForm,string _url,int _startChapter){
            myForm = _myForm;
            url = _url;
            startChapter=_startChapter;
            charSet = "utf-8";

        }
        public BookLoad(mainForm _myForm, string _url,int _startChapter,string _charSet)
        {
            myForm = _myForm;
            url = _url;
            startChapter=_startChapter;
            charSet = _charSet;
        }
        /// <summary>
        /// 下载数据
        /// </summary>
        public void load()
        {

            try
            {
                string bookURL = url;
                string onlyhost=GetData.getOnlyHostName(bookURL);
                //获取正则表达式，返回null就退出
                BookReg bookReg = getHostRegConfig(onlyhost);
                if (bookReg == null)
                    return;
                string html = GetData.getHtml(bookURL, bookReg.charSet);
                //获取文章列表
                //正则表达式
                //文章列表
                //  <dd><a href="/18_18350/1019756.html">第1章 我是谁</a></dd>
                string sRegBookName = bookReg.BookName;
                string sRegList = bookReg.List;
                string sRegContent =bookReg.Content;
                string sExceptString = bookReg.ExceptString;


                //获取小说名称，用于存储
                string bookName = GetData.getTitle(html, sRegBookName);
                if (bookName == "")
                {
                    setWorkStatus("错误", 0, "获取书籍名称失败，请检查配置中第二行正则表达式");
                    return;
                }

                //小说Txt内容
                string content = "";
                //获取章节列表
                ChapterList bookinfo = GetData.getList(html, sRegList);
                //最大的索引值
                int maxIndex = bookinfo.Title.Count;
                if (maxIndex == 0)
                {
                    setWorkStatus("错误", 0, "获取章节列表失败，请检查配置中第三行正则表达式");
                    return;
                }
                else
                {
                    setWorkStatus("目录获取完成，共有" + maxIndex + "章。", 0, "");
                }
                //获取每一页内容
                string result = "";
                //需要下载的章节数
                int amount = maxIndex - startChapter + 1;
                //下载完成数
                int done = 0;
                //从指定的开始章节下载
                for (int i = startChapter-1; i < maxIndex; i++)
                {
                    
                    string realChapterURL = GetData.getRealChapterURL(bookURL, bookinfo.URL[i].ToString());
                    try
                    {
                        //如果获取失败，10秒后重试
                        html = GetData.getHtml(realChapterURL, bookReg.charSet);
                    }
                    catch (Exception ex)
                    {
                        if (ex.Message != "正在中止线程。")
                        {
                            setWorkStatus("", 0, "下载时:" + ex.Message + "20秒后重试一次");
                            Thread.Sleep(20000);
                            html = GetData.getHtml(realChapterURL, bookReg.charSet);
                        }
                    }
                    content = GetData.getContent(html, sRegContent, sExceptString);
                    content = GetData.clearHTML(content, sExceptString);
                    //显示的结果信息
                    result = result + Environment.NewLine + bookinfo.Title[i];
                    //todo:无法直接用正则表达式替换，因此将其切分为N个字符串再连接，Environment.NewLine能够实现win和linux兼容
                    string[] sArray = Regex.Split(content, "<P>");
                    for (int j = 0; j < sArray.Length; j++)
                    {
                        result = result + Environment.NewLine + sArray[j];
                    }
                    done++;
                    setWorkStatus("下载中，完成" + Convert.ToString(done) + "/" + Convert.ToString(amount), (int)((done) * 100 / amount), bookinfo.Title[i].ToString());
                }
                bookName = bookName.Replace("\n", "").Replace(" ", ""); //清理书名中的空白字符
                string path = @""+bookName+".txt";
                StreamWriter wt = new StreamWriter(path, false);
                wt.WriteLine(result);
                wt.Close();
                setWorkStatus("下载打包完成！", 100, "书籍成功打包为'"+bookName+".txt'");

            }
            catch (Exception ex)
            {
                if (ex.Message != "正在中止线程。")
                    setWorkStatus("", 0, "下载时:" + ex.Data + ex.Message);

            }
           
        }

        /// <summary>
        /// 获取网页HTML
        /// </summary>
        public void getTestPage()
        {
            string bookURL = url;
            string html=GetData.getHtml(url,charSet);
            setWorkStatus("测试网页读取成功", 0, html);
        }
        /// <summary>
        /// 获取正则表达式配置
        /// </summary>
        /// <param name="host"></param>
        /// <returns></returns>
        private BookReg getHostRegConfig(string host)
        {
            BookReg bookReg = new BookReg();
            //获取当前目录
            string regPath = System.Environment.CurrentDirectory + "/config/" + host + ".json";
            //检查是否存在对应的文件 config/host.json文件
            if (!File.Exists(regPath))
            {
                //如果本地没有，就到网上去下载一个，并保存到本地
                //下载网址根目录为:
                setWorkStatus("未找到本地配置", 0, "在config文件夹未找到名为" + host + ".json的配置文件。\n");
                try
                {
                    setWorkStatus("从服务器获取配置", 0, "");
                    string remote = "http://fangrenfu.gitee.io/txtdown/" + host + ".json";
                    string remoteHtml = GetData.getHtml(remote, "utf-8");
                    //用substring去掉文件开头的BOM标记
                    remoteHtml = RemoveBom(remoteHtml, Encoding.UTF8);
                    string[] sArray = Regex.Split(remoteHtml, "\n");
                    bookReg.charSet = sArray[0];
                    bookReg.BookName = sArray[1];
                    bookReg.List = sArray[2];
                    bookReg.Content = sArray[3];
                    bookReg.ExceptString = sArray[4];

                    string path = @"config/" + host + ".json";
                    StreamWriter wt = new StreamWriter(path, false);
                    string result = bookReg.charSet;
                    for (int i = 1; i < 5; i++)
                        result += Environment.NewLine + sArray[i];

                    wt.WriteLine(result);
                    wt.Close();
                    setWorkStatus("配置获取成功", 0, host+"的配置获取成功，并存入config文件夹。");
                    return bookReg;
                }
                catch (Exception ex) {
                    setWorkStatus("错误", 0, "服务器获取信息失败。");
                    return null;
                }       
            }
            else
            {
                //有的话读取文件
                using (StreamReader sr = new StreamReader(regPath))
                {
                    //依次读取每一行
                    bookReg.charSet = sr.ReadLine();
                    bookReg.BookName = sr.ReadLine();
                    bookReg.List = sr.ReadLine();
                    bookReg.Content = sr.ReadLine();
                    bookReg.ExceptString = sr.ReadLine();
                }
            }
            return bookReg;
        }

        /// <summary>
        /// 设置工作状态，用于多线程
        /// </summary>
        /// <param name="status">状态栏</param>
        /// <param name="amount">进度条值</param>
        /// <param name="message">反馈信息</param>
        public void setWorkStatus(string status, int amount, string message)
        {
            //来设置工作状态，解决多线程下不能设置问题
            try
            {
                if (myForm.InvokeRequired)
                {
                    SetTextCallback d = new SetTextCallback(setWorkStatus);
                    myForm.Invoke(d, new object[] { status, amount, message });

                }
                else
                {
                    if (status != "")
                    {
                        myForm.statusText.Text = status;
                        //如果出现错误，停止
                        if (status == "错误")
                        {
                            myForm.isLoading = false;
                            myForm.btStart.Text = "开始";
                            myForm.tbMessage.Text = message;
                            return;
                        }
                        if (status == "测试网页读取成功")
                        {
                            myForm.tbTestResult.Text = message;
                            return;
                        }
                    }

                    if (amount != 0)
                    {
                        myForm.statusProgress.Value = amount;
                        if (amount == 100)
                        {
                            myForm.isLoading = false;
                            myForm.btStart.Text = "开始";
                        }
                    }
                    if (message != "")
                        myForm.tbMessage.Text = message +Environment.NewLine+ myForm.tbMessage.Text;
                }
            }
            catch (Exception ex)
            {
                string info = ex.Message + ex.Data;
                // MessageBox.Show("设置工作状态" + ex.Message + ex.Message + ex.Data);
                setWorkStatus("", 0, "设置工作状态错误" + ex.Message + ex.Data + "\r\n");

            }

        }

        /// <summary>
        /// 移除BOM标记
        /// </summary>
        /// <param name="desc"></param>
        /// <param name="encode"></param>
        /// <returns></returns>
        public static string RemoveBom(String desc, Encoding encode)
        {
            string bomString = encode.GetString(encode.GetPreamble());
            if (!string.IsNullOrEmpty(bomString) && desc.StartsWith(bomString))
            {
                desc = desc.Remove(0, bomString.Length);
            }
            return desc;
        }
    }
}
